package club.zhilin.guli.service.ucenter.controller.portal;

import club.zhilin.guli.common.base.domain.JwtInfo;
import club.zhilin.guli.common.base.result.CommonResultCodeEnum;
import club.zhilin.guli.common.base.util.HttpClientUtils;
import club.zhilin.guli.common.base.util.JwtUtil;
import club.zhilin.guli.service.base.exception.GlobalException;
import club.zhilin.guli.service.ucenter.config.UserCenterProperties;
import club.zhilin.guli.service.ucenter.domain.Member;
import club.zhilin.guli.service.ucenter.service.IMemberService;
import com.google.gson.Gson;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author zhilin
 * @since 2022/2/27 15:56
 */
@Slf4j
@Controller
@RequestMapping("/portal/ucenter/wx")
@EnableConfigurationProperties(value = {UserCenterProperties.class})
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class WxController {

    private final UserCenterProperties userCenterProperties;
    private final IMemberService iMemberService;

    /**
     * 生成微信登录二维码
     *
     * @return string
     */
    @GetMapping("/login")
    public String generateQr(@ApiIgnore HttpSession session) {
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";
        String encode;
        try {
            encode = URLEncoder.encode(userCenterProperties.getRedirectUri(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error(ExceptionUtils.getMessage(e));
            throw new GlobalException(CommonResultCodeEnum.URL_ENCODE_ERROR);
        }
        // state
        String state = UUID.randomUUID().toString();
        // 将state存在session中
        session.setAttribute("wx-open-state", state);
        String codeUrl = String.format(baseUrl, userCenterProperties.getAppId(), encode, state);
        return "redirect:" + codeUrl;
    }

    /**
     * 回调函数
     *
     * @param code    临时票据
     * @param state   随机码
     * @param session 会话作用域
     * @return string
     */
    @GetMapping("/callback")
    @SuppressWarnings({"unchecked"})
    public String callback(String code, String state, @ApiIgnore HttpSession session) {
        if (StringUtils.isEmpty(code) || StringUtils.isEmpty(state)) {
            log.error("非法回调请求");
            throw new GlobalException(CommonResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }
        String sessionState = (String) session.getAttribute("wx-open-state");
        if (!state.equals(sessionState)) {
            log.error("非法回调请求");
            throw new GlobalException(CommonResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }
        // 携带 code 临时票据,和appId以及appSecret请求accessToken
        String accessToken = "https://api.weixin.qq.com/sns/oauth2/access_token";
        // 组装参数
        Map<String, String> params = new HashMap<>();
        params.put("appid", userCenterProperties.getAppId());
        params.put("secret", userCenterProperties.getAppSecret());
        params.put("code", code);
        params.put("grant_type", "authorization_code");
        HttpClientUtils clientUtils = new HttpClientUtils(accessToken, params);
        String result;
        try {
            // 组装完整的url,发送请求
            clientUtils.get();
            result = clientUtils.getContent();
        } catch (IOException e) {
            log.error("获取accessToken失败");
            throw new GlobalException(CommonResultCodeEnum.FETCH_ACCESS_TOKEN_FAILD);
        }
        Gson gson = new Gson();
        HashMap<String, Object> resultHash = gson.fromJson(result, HashMap.class);
        Object error = resultHash.get("errcode");
        if (error != null) {
            Object message = resultHash.get("errmsg");
            log.error("获取 accessToken 失败,code: {},message: {}", error.toString(), message);
            throw new GlobalException(CommonResultCodeEnum.FETCH_ACCESS_TOKEN_FAILD);
        }
        // 解析出结果中的accessToken
        String successAccessToken = (String) resultHash.get("access_token");
        String openId = (String) resultHash.get("openid");
        // 获取个人信息
        // else:如果当前用户已存在本地数据库中，则直接使用当前用户的信息登录(生成jwt)
        Member member = iMemberService.getByOpenId(openId);
        if (member == null) {
            // if: 如果当前用户不存在,则去微信资源服务器获取用户个人信息(携带access_token)
            String baseUrl = "https://api.weixin.qq.com/sns/userinfo";
            Map<String, String> baseUrlParamsInfo = new HashMap<>();
            baseUrlParamsInfo.put("access_token", successAccessToken);
            baseUrlParamsInfo.put("openid", openId);
            clientUtils = new HttpClientUtils(baseUrl, baseUrlParamsInfo);
            String resultUserInfo;
            try {
                clientUtils.get();
                resultUserInfo = clientUtils.getContent();
            } catch (IOException e) {
                log.error(ExceptionUtils.getMessage(e));
                throw new GlobalException(CommonResultCodeEnum.FETCH_ACCESS_TOKEN_FAILD);
            }
            HashMap<String, Object> resultInfoMap = gson.fromJson(resultUserInfo, HashMap.class);
            error = resultInfoMap.get("errcode");
            if (error != null) {
                Object message = resultHash.get("errmsg");
                log.error("获取 accessToken 失败,code: {},message: {}", error.toString(), message);
                throw new GlobalException(CommonResultCodeEnum.FETCH_ACCESS_TOKEN_FAILD);
            }
            String nickname = (String) resultInfoMap.get("nickname");
            String avatar = (String) resultInfoMap.get("headimgurl");
            Double sex = (Double) resultInfoMap.get("sex");
            member = new Member();
            member.setOpenid(openId);
            member.setNickname(nickname);
            member.setAvatar(avatar);
            member.setSex(sex.intValue());
            iMemberService.save(member);
        }
        JwtInfo info = new JwtInfo();
        info.setAvatar(member.getAvatar());
        info.setNickname(member.getNickname());
        info.setId(member.getId());
        String token = JwtUtil.createJwtToken(info, 1800);
        // 则在本地数据库中插入当前微信用户的信息(使用微信账号在本地服务器注册)
        return "redirect:http://localhost:3000?token=" + token;
    }
}
